﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Timers;
using HighLowBase;
using HighLowBase.Entity;
using HighLowTriple7.Entity;

namespace HighLowTriple7
{
    public static class Triple7
    {
        #region
        class MyGame
        {
            public int AccountId { get; set; }
            public string AccountName { get; set; }
            public int AccountType { get; set; }
            public string AccessToken { get; set; }
            public HiLoTriple7 GameTriple7 { get; set; }

            public DateTime CreatedDate { get; set; }
            public DateTime ModifiedDate { get; set; }
            public DateTime ActivatedDate { get; set; }

            public MyGame()
            {
                ModifiedDate = CreatedDate = DateTime.Now;
            }
        }
        #endregion

        #region Properties
        private static readonly object objLock = new object();

        private static readonly object objAddJackpotLock = new object();

        private static readonly Dictionary<string, MyGame> GameTriple7Dic = new Dictionary<string, MyGame>();

        private static readonly Dictionary<int, string> AccessTokenDic = new Dictionary<int, string>();

        private static System.Timers.Timer TimerTick = null;

        private static bool IsUpdate = false;

        private static Thread ThreadUpdateGame = null;

        private static Thread ThreadUpdateRateConfig = null;

        private static System.Timers.Timer TimerCheckUser = null;

        private static double TotalAuForJackpot = 0;

        private static double TotalAgForJackpot = 0;
        #endregion

        #region Private method
        private static void Instance()
        {
            if (HiLoTriple7.GameTriple7 == null)
            {
                lock (objLock)
                {
                    if (HiLoTriple7.GameTriple7 == null)
                    {
                        HiLoTriple7.GameTriple7 = HiLoGame.LoadGameInfo(HiLoGame.GameId.Triple7Id);

                        if (TimerTick == null)
                        {
                            TimerTick = new System.Timers.Timer()
                            {
                                Interval = 1000,
                                Enabled = true
                            };
                            TimerTick.Elapsed += TimerTick_Elapsed;
                        }
                        if (TimerCheckUser == null)
                        {
                            TimerCheckUser = new System.Timers.Timer()
                            {
                                Interval = 1000 * 60 * 60 * 4,
                                Enabled = true
                            };
                            TimerCheckUser.Elapsed += TimerCheckUser_Elapsed;
                        }
                    }
                }
            }
        }

        private static void TimerTick_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Console.WriteLine("Tick");
            if (IsUpdate)
                return;
            IsUpdate = true;
            try
            {
                #region Update game
                if (ThreadUpdateGame == null || !ThreadUpdateGame.IsAlive)
                {
                    ThreadUpdateGame = new Thread(() =>
                    {
                        try
                        {
                            Console.WriteLine("UpdateGame");
                            GameBaseEntity gameBase = HighLowBase.HiLoGame.LoadGameInfo(HiLoGame.GameId.Triple7Id);
                            lock (HiLoTriple7.GameTriple7)
                            {
                                HiLoTriple7.GameTriple7 = gameBase;
                            }
                        }
                        catch (Exception ex)
                        {
                            //log
                        }
                        try
                        {
                            Thread.Sleep(10000);
                        }
                        catch
                        {
                        }
                    });
                    ThreadUpdateGame.Start();
                }
                #endregion

                #region Update rate config
                if (ThreadUpdateRateConfig == null || !ThreadUpdateRateConfig.IsAlive)
                {
                    ThreadUpdateRateConfig = new Thread(() =>
                    {
                        try
                        {
                            Console.WriteLine("UpdatRateConfig");
                            HiLoRedis.SetRateConfigTriple7(HiLoGame.GetRateConfigTriple7DB());

                        }
                        catch (Exception ex)
                        {
                            //log
                        }
                        try
                        {
                            Thread.Sleep(10000);
                        }
                        catch
                        {
                        }
                    });
                    ThreadUpdateRateConfig.Start();
                }

                #endregion
            }
            catch (Exception ex)
            {
                //Am tham lang le log
            }
            IsUpdate = false;
        }

        private static void TimerCheckUser_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                TimerCheckUser.Enabled = false;
                DateTime dt = DateTime.Now;
                if (dt.Hour != 2)
                    return;

                List<MyGame> list = null;

                lock (objLock)
                {
                    list = GameTriple7Dic.Values.ToList();
                }

                int day = list.Count / 500 - 7;
                if (day >= 0)
                    day = -1;
                dt = dt.AddDays(day);
                for (int i = 0; i < list.Count; i++)
                {
                    var item = list[i];
                    if (item.ActivatedDate < dt)
                    {
                        lock (objLock)
                        {
                            GameTriple7Dic.Remove(item.AccessToken);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                //log
            }
            finally
            {
                TimerCheckUser.Enabled = true;
            }
        }

        private static string GetToken(int accountId, string accountName, int accountType)
        {
            var md5 = new MD5CryptoServiceProvider();

            byte[] key = md5.ComputeHash(Encoding.ASCII.GetBytes(DateTime.Now.ToString()));

            TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
            des.Key = key;
            des.Mode = CipherMode.ECB;
            des.Padding = PaddingMode.PKCS7;

            byte[] input = Encoding.ASCII.GetBytes(string.Format("{0}-{1}-{2}", accountId, accountName, accountType));
            byte[] buffer = new byte[0];
            try { buffer = des.CreateEncryptor().TransformFinalBlock(input, 0, input.Length); }
            finally
            {
                des.Clear();
                md5.Clear();
            }

            return Convert.ToBase64String(buffer);

        }

        private static HiLoTriple7 GetHiLoTriple7(string accessToken)
        {
            try
            {
                var myGame = GameTriple7Dic[accessToken];
                myGame.ActivatedDate = DateTime.Now;
                return myGame.GameTriple7;
            }
            catch (Exception)
            {
                throw new Exception("AccessToken không hợp lệ");
            }
        }

        private static void AddForJackpot(bool isAu, int bet)
        {
            double total = 0;

            if (isAu)
            {
                total = TotalAuForJackpot + bet;
                if (total < HiLoTriple7.GameTriple7.JackpotBlockAu)
                {
                    TotalAuForJackpot = total;
                    return;
                }
            }
            else
            {
                total = TotalAgForJackpot + bet;
                if (total < HiLoTriple7.GameTriple7.JackpotBlockAg)
                {
                    TotalAgForJackpot = total;
                    return;
                }
            }

            lock (objAddJackpotLock)
            {
                try
                {
                    if (isAu)
                    {
                        int au = HiLoTriple7.GameTriple7.JackpotJumpAu * (int)(total / HiLoTriple7.GameTriple7.JackpotBlockAu);
                        if (HiLoGame.AddJackpotAuAg(HiLoTriple7.GameTriple7.Id, au, 0) > 0)
                            TotalAuForJackpot = total % HiLoTriple7.GameTriple7.JackpotJumpAu;
                    }
                    else
                    {
                        int ag = HiLoTriple7.GameTriple7.JackpotJumpAg * (int)(total / HiLoTriple7.GameTriple7.JackpotBlockAg);
                        if (HiLoGame.AddJackpotAuAg(HiLoTriple7.GameTriple7.Id, 0, ag) > 0)
                            TotalAgForJackpot = total % HiLoTriple7.GameTriple7.JackpotJumpAg;
                    }
                }
                catch (Exception ex)
                {
                    //log
                }
            }
        }

        #endregion Private method

        #region Public method
        public static string Instance(int accountId, string accountName, int accountType)
        {
            string accessToken = null;

            Instance();

            MyGame game = null;
            lock (objLock)
            {
                try
                {
                    accessToken = AccessTokenDic[accountId];
                }
                catch
                {
                    accessToken = null;
                }

                if (accessToken == null)
                {
                    #region Tao moi
                    accessToken = GetToken(accountId, accountName, accountType);
                    game = new MyGame()
                    {
                        AccountId = accountId,
                        AccountName = accountName,
                        AccountType = accountType,
                        AccessToken = accessToken,
                        GameTriple7 = new HiLoTriple7(accountId, accountName, accountType),
                    };

                    try
                    {
                        GameTriple7Dic.Add(accessToken, game);
                    }
                    catch (Exception)
                    {
                        GameTriple7Dic[accessToken] = game;
                    }
                    try
                    {
                        AccessTokenDic.Add(accountId, accessToken);
                    }
                    catch (Exception)
                    {
                        AccessTokenDic[accountId] = accessToken;
                    }
                    #endregion Tao moi

                    return accessToken;
                }
                else
                {
                    #region Tao access token moi
                    game = GameTriple7Dic[accessToken];
                    GameTriple7Dic.Remove(accessToken);

                    accessToken = GetToken(accountId, accountName, accountType);

                    game.AccessToken = accessToken;
                    GameTriple7Dic.Add(accessToken, game);

                    AccessTokenDic[accountId] = accessToken;
                    #endregion Tao access token moi

                    return accessToken;
                }
            }
        }

        public static RateConfigEntity GetRateConfig(int betValue, string accessToken)
        {
            return GetHiLoTriple7(accessToken).GetRateConfig(betValue);
        }

        public static int GetGameStatus(string accessToken)
        {
            return GetHiLoTriple7(accessToken).GetGameStatus();
        }

        public static int GetRound(string accessToken)
        {
            return GetHiLoTriple7(accessToken).GetRound();
        }

        public static int GetColumn1Value(string accessToken)
        {
            return GetHiLoTriple7(accessToken).GetColumn1Value();
        }

        public static int GetColumn2Value(string accessToken)
        {
            return GetHiLoTriple7(accessToken).GetColumn2Value();
        }

        public static int GetColumn3Value(string accessToken)
        {
            return GetHiLoTriple7(accessToken).GetColumn3Value();
        }

        public static int GetAuCurrent(string accessToken)
        {
            return GetHiLoTriple7(accessToken).GetAuCurrent();
        }

        public static int GetAgCurrent(string accessToken)
        {
            return GetHiLoTriple7(accessToken).GetAgCurrent();
        }

        public static int GetLastValue(string accessToken)
        {
            return GetHiLoTriple7(accessToken).GetLastValue();
        }

        public static int GetExtraLife(string accessToken)
        {
            return GetHiLoTriple7(accessToken).GetExtraLife();
        }

        public static int GetOutOf(string accessToken)
        {
            return GetHiLoTriple7(accessToken).GetOutOf();
        }

        public static int GetNudges(string accessToken)
        {
            return GetHiLoTriple7(accessToken).GetNudges();
        }

        public static int GetFreeSpin(string accessToken)
        {
            return GetHiLoTriple7(accessToken).GetFreeSpin();
        }

        public static Triple7SessionInfo GetSessionInfo(string accessToken)
        {
            HiLoTriple7 hiLoTriple7 = GetHiLoTriple7(accessToken);

            Triple7SessionInfo info = new Triple7SessionInfo()
            {
                GameStatus = hiLoTriple7.GetGameStatus(),
                Round = hiLoTriple7.GetRound(),
                AuBet = hiLoTriple7.GetAuBet(),
                AgBet = hiLoTriple7.GetAgBet(),
                AuCurrent = hiLoTriple7.GetAuCurrent(),
                AgCurrent = hiLoTriple7.GetAgCurrent(),
                Column1Value = hiLoTriple7.GetColumn1Value(),
                Column2Value = hiLoTriple7.GetColumn2Value(),
                Column3Value = hiLoTriple7.GetColumn3Value(),
                ExtraLife = hiLoTriple7.GetExtraLife(),
                OutOf = hiLoTriple7.GetOutOf(),
                Nudges = hiLoTriple7.GetNudges(),
                Jackpot = hiLoTriple7.GetJackpot()
            };
            return info;
        }

        public static Triple7GameInfo GetGameInfo(string accessToken)
        {
            Instance();
            return new Triple7GameInfo()
            {
                Id = HiLoTriple7.GameTriple7.Id,
                Name = HiLoTriple7.GameTriple7.Name,
                MaximumBet = HiLoTriple7.GameTriple7.MaximumBet,
                MinimumBet = HiLoTriple7.GameTriple7.MinimumBet,
                MinimumValue = HiLoTriple7.GameTriple7.MinimumValue,
                MaximumValue = HiLoTriple7.GameTriple7.MaximumValue,
                Jackpot = (HiLoTriple7.GameTriple7.JackpotAu + HiLoTriple7.GameTriple7.JackpotAg)
            };
        }

        public static void PlayerBetAu(int auBet, string accessToken)
        {
            GetHiLoTriple7(accessToken).PlayerBetAu(auBet);
            AddForJackpot(true, auBet);
        }

        public static void PlayerBetAg(int agBet, string accessToken)
        {
            GetHiLoTriple7(accessToken).PlayerBetAg(agBet);
            AddForJackpot(false, agBet);
        }

        public static void PlayerSelectHigh(int column, string accessToken)
        {
            GetHiLoTriple7(accessToken).PlayerSelectHigh(column);
        }

        public static void PlayerSelectLow(int column, string accessToken)
        {
            GetHiLoTriple7(accessToken).PlayerSelectLow(column);
        }

        public static void PlayerSelectNudges(string accessToken)
        {
            GetHiLoTriple7(accessToken).PlayerSelectNudges();
        }

        public static void PlayerCollect(string accessToken)
        {
            GetHiLoTriple7(accessToken).PlayerCollect();
        }

        public static List<HelpEntity> GetHelpInfoList(int type)
        {
            return HiLoGame.GetHelpInfoList(HiLoGame.GameId.Triple7Id, type);
        }

        #endregion
    }
}
